package com.example.util.encryption;

import com.example.core.error.EncryptionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.*;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 加签验签,私钥加签,公钥验签
 * 加密解密,公钥加密,私钥解密/私钥加密,公钥解密
 *
 * @author wangkai
 */
public class Sha1WithRSAUtil {

    private static Logger log= LoggerFactory.getLogger(Sha1WithRSAUtil.class);

    private static final String KEY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
    public static final String CHARSET_NAME = "UTF-8";

    /**
     * 加签
     *
     * @param privateKey
     * @param content
     * @return
     */
    public static String sign(String privateKey, String content) {
        if(!(privateKey!=null&&privateKey.trim().length()>0)){
            log.error("==========私钥为空加签失败========");
            throw new EncryptionException("私钥为空加签失败");
        }
        if(!(content!=null&&content.trim().length()>0)){
            log.error("==========加签数据为空加签失败========");
            throw new EncryptionException("加签数据为空加签失败");
        }
        try {
            if(log.isInfoEnabled()) log.info("==========数据加签处理========");
            PrivateKey priKey = getPrivateKey(privateKey);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(priKey);
            signature.update(content.getBytes(CHARSET_NAME));
            return EncryptionUtil.encryptBASE64(signature.sign());
        } catch (Exception e) {
            log.error("==========数据加签处理异常：{}",e.getMessage());
            throw new EncryptionException("数据加签处理异常");
        }
    }


    /**
     * 验证签名
     *
     * @param signString 私钥签名
     * @param sign       验证数据
     * @return
     * @throws Exception
     */
    public static boolean verify(String public_key, String signString, String sign){

        if(!(public_key!=null&&public_key.trim().length()>0)){
            log.error("==========公钥为空验证失败========");
            throw new EncryptionException("公钥为空验证失败");
        }
        if(!(signString!=null&&signString.trim().length()>0)){
            log.error("==========私钥签名为空验证失败========");
            throw new EncryptionException("私钥签名为空验证失败");
        }
        if(!(sign!=null&&sign.trim().length()>0)){
            log.error("==========验证数据为空验证失败========");
            throw new EncryptionException("验证数据为空验证失败");
        }
        try {
            byte[] data = EncryptionUtil.decryptBASE64(signString);
            byte[] signs = sign.getBytes(CHARSET_NAME);
            if(log.isInfoEnabled()) log.info("==========验证数据处理========");
            PublicKey pubKey = getPublicKey(public_key);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(pubKey);
            signature.update(signs);
            return signature.verify(data);
        } catch (Exception e) {
            log.error("==========验证数据处理异常：{}",e.getMessage());
            throw new EncryptionException("验证数据处理异常");
        }
    }


    /**
     * 私钥加密数据
     *
     * @param originData
     * @return
     */
    public static String encodeDataPrivateKey(String private_key, String originData) {
        if(!(private_key!=null&&private_key.trim().length()>0)){
            log.error("==========私钥为空加密失败========");
            throw new EncryptionException("私钥为空加密失败");
        }
        if(!(originData!=null&&originData.trim().length()>0)){
            log.error("==========加密数据为空加密失败========");
            throw new EncryptionException("加密数据为空加密失败");
        }
        try {
            if(log.isInfoEnabled()) log.info("==========私钥加密数据========");
            // 取私钥对象
            PrivateKey priKey = getPrivateKey(private_key);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, priKey);
            byte[] bytesEncrypt = cipher.doFinal(originData.getBytes(CHARSET_NAME));
            //Base64编码
            return EncryptionUtil.encryptBASE64(bytesEncrypt);
        } catch (Exception e) {
            log.error("==========私钥加密数据异常：{}",e.getMessage());
            throw new EncryptionException("私钥加密数据异常");
        }
    }

    /**
     * 公钥加密数据
     *
     * @param public_key
     * @param originData
     * @return
     */
    public static String encodeDataPublicKey(String public_key, String originData) {
        if(!(public_key!=null&&public_key.trim().length()>0)){
            log.error("==========公钥为空加密失败========");
            throw new EncryptionException("公钥为空加密失败");
        }
        if(!(originData!=null&&originData.trim().length()>0)){
            log.error("==========加密数据为空加密失败========");
            throw new EncryptionException("加密数据为空加密失败");
        }
        try {
            if(log.isInfoEnabled())  log.info("==========公钥加密数据========");
            // 取公钥对象
            PublicKey pubKey = getPublicKey(public_key);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] bytesEncrypt = cipher.doFinal(originData.getBytes(CHARSET_NAME));
            //Base64编码
            return EncryptionUtil.encryptBASE64(bytesEncrypt);
        } catch (Exception e) {
            log.error("==========公钥加密数据异常：{}",e.getMessage());
            throw new EncryptionException("公钥加密数据异常");
        }
    }

    /**
     * 私钥解密数据
     *
     * @param encodeData
     * @return
     */
    public static String decodeDataPrivateKey(String private_key, String encodeData) {
        if(!(private_key!=null&&private_key.trim().length()>0)){
            log.error("==========私钥为空解密失败========");
            throw new EncryptionException("私钥为空解密失败");
        }
        if(!(encodeData!=null&&encodeData.trim().length()>0)){
            log.error("==========解密数据为空解密失败========");
            throw new EncryptionException("解密数据为空解密失败");
        }
        try {
            if(log.isInfoEnabled())  log.info("==========私钥解密数据========");
            // 取私钥对象
            PrivateKey privateKey = getPrivateKey(private_key);
            //Base64解码
            byte[] bytesEncrypt = EncryptionUtil.decryptBASE64(encodeData);
            //加密
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] bytesDecrypt = cipher.doFinal(bytesEncrypt);
            return new String(bytesDecrypt);
        } catch (Exception e) {
            log.error("==========私钥解密数据异常：{}",e.getMessage());
            throw new EncryptionException("私钥解密数据异常");
        }
    }

    /**
     * 公钥解密数据
     *
     * @param encodeData
     * @return
     */
    public static String decodeDataPublicKey(String public_key, String encodeData) {
        if(!(public_key!=null&&public_key.trim().length()>0)){
            log.error("==========公钥为空解密失败========");
            throw new EncryptionException("公钥为空解密失败");
        }
        if(!(encodeData!=null&&encodeData.trim().length()>0)){
            log.error("==========解密数据为空解密失败========");
            throw new EncryptionException("解密数据为空解密失败");
        }
        try {
            if(log.isInfoEnabled())  log.info("==========公钥解密数据========");
            // 取公钥对象
            PublicKey publicKey = getPublicKey(public_key);
            //Base64解码
            byte[] bytesEncrypt = EncryptionUtil.decryptBASE64(encodeData);
            //加密
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            byte[] bytesDecrypt = cipher.doFinal(bytesEncrypt);
            return new String(bytesDecrypt);
        } catch (Exception e) {
            log.error("==========公钥解密数据异常：{}",e.getMessage());
            throw new EncryptionException("公钥解密数据异常");
        }
    }


    /**
     * 将字符串转换成公钥
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key){
        if(!(key!=null&&key.trim().length()>0)){
            log.error("==========密钥字符串为空公钥转换失败========");
            throw new EncryptionException("密钥字符串为空公钥转换失败");
        }
        try {
            if(log.isInfoEnabled()) log.info("==========转换公钥========");
            byte[] keyBytes = EncryptionUtil.decryptBASE64(key);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return publicKey;
        }catch (Exception e){
            log.error("==========转换公钥异常：{}",e.getMessage());
            throw new EncryptionException("转换公钥异常");
        }
    }

    /**
     * 将字符串转换成私钥
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key)   {
        if(!(key!=null&&key.trim().length()>0)){
            log.error("==========密钥字符串为空私钥转换失败========");
            throw new EncryptionException("密钥字符串为空私钥转换失败");
        }
        try{
            if(log.isInfoEnabled())  log.info("==========转换私钥========");
            byte[] keyBytes = EncryptionUtil.decryptBASE64(key);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            return privateKey;
        }catch (Exception e){
            log.error("==========转换私钥异常：{}",e.getMessage());
            throw new EncryptionException("转换私钥异常");
        }
    }


    /**
     *  获取文件私钥
     * @param path 文件路径
     * @param pathFlag false resources目录下文件地址，true 文件为绝对地址
     * @return
     */
    public static String getPathPrivateKey(String path,boolean... pathFlag) {
        InputStream inputStream =null;
        InputStreamReader reader=null;
        BufferedReader br = null;
        boolean flag=pathFlag==null?false:pathFlag.length==1&&pathFlag[0]==true?true:false;
        try {
            if(log.isInfoEnabled()) log.info("==========读取密钥========");
            if(!flag){
                inputStream= Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
                reader=new  InputStreamReader(inputStream);
                br = new BufferedReader(reader);
            }else{
                br = new BufferedReader(new FileReader(path));
            }
            String s = br.readLine();
            StringBuffer privatekey = new StringBuffer();
            s = br.readLine();
            while (s.charAt(0) != '-') {
                privatekey.append(s + "\r");
                s = br.readLine();
            }
            return privatekey.toString();
        } catch (Exception e) {
            log.error("==========读取密钥异常：{}",e.getMessage());
            throw new EncryptionException("读取密钥异常");
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    log.error("==========文件关闭异常：{}",e.getMessage());
                    throw new EncryptionException("读取密钥异常");
                }
            }
            if(!flag){
                if(reader!=null){
                    try {
                        reader.close();
                        inputStream.close();
                    } catch (IOException e) {
                        log.error("==========文件关闭异常：{}",e.getMessage());
                        throw new EncryptionException("读取密钥异常");
                    }
                }
            }
        }
    }
    /**
     *  获取文件公钥
     * @param path 文件路径
     * @param pathFlag false resources目录下文件地址，true 文件为绝对地址
     * @return
     */
    public static String getPathPublicKey(String path,boolean... pathFlag) {
        return getPathPrivateKey(path,pathFlag);
    }




//    public static void main(String[] args) {
//        String privateKeyPath = "F:\\规划项目\\spring.startup\\src\\main\\resources\\rsa_private_key.pem";
//        String publicKeyPath = "F:\\规划项目\\spring.startup\\src\\main\\resources\\rsa_public_key.pem";
//        String jiami = encodeDataPublicKey(getPathPublicKey(publicKeyPath), "食之无味,弃之可惜");
//        System.out.println("加密后的数据:" + jiami);
//        String jiemi = decodeDataPrivateKey(getPathPrivateKey(privateKeyPath), jiami);
//        System.out.println("解密后的数据:" + jiemi);
//    }

//    public static void main(String[] args) {
//
//        String privateKeyPath = "F:\\规划项目\\spring.startup\\src\\main\\resources\\rsa_private_key.pem";
//        String publicKeyPath = "F:\\规划项目\\spring.startup\\src\\main\\resources\\rsa_public_key.pem";
//
//        /**
//         * 私钥加签
//         * 公钥验签
//         */
//        try {
//            String sign = "食之无味,弃之可惜";
////            String sign = "memberid=1232&changAmount=123&changeWay=1";
//            String signString = sign(getPathPrivateKey(privateKeyPath), sign);
//            System.out.println("加签的数据:" + signString);
//            boolean flag = verify(getPathPublicKey(publicKeyPath), signString, sign);
//            System.out.println("验证结果:" + flag);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
}